<template>
  <div class="matrix-2d">
    <h1>2D矩阵变换</h1>
    <p>通过矩阵变换，我们可以实现图形的平移、旋转和缩放。这个示例展示了如何使用WebGL中的矩阵变换来操作2D图形。</p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>矩阵变换示例</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="canvas" width="400" height="400"></canvas>
        <div class="control-panel">
          <el-form label-width="100px">
            <el-form-item label="平移 X">
              <el-slider v-model="translateX" :min="-1" :max="1" :step="0.1" @input="updateTransform" />
            </el-form-item>
            <el-form-item label="平移 Y">
              <el-slider v-model="translateY" :min="-1" :max="1" :step="0.1" @input="updateTransform" />
            </el-form-item>
            <el-form-item label="旋转">
              <el-slider v-model="rotation" :min="0" :max="360" @input="updateTransform" />
            </el-form-item>
            <el-form-item label="缩放 X">
              <el-slider v-model="scaleX" :min="-1" :max="1" :step="0.1" @input="updateTransform" />
            </el-form-item>
            <el-form-item label="缩放 Y">
              <el-slider v-model="scaleY" :min="-1" :max="1" :step="0.1" @input="updateTransform" />
            </el-form-item>
          </el-form>
          <div class="transform-info">
            <p>当前变换：</p>
            <p>平移({{ translateX }}, {{ translateY }}) 旋转{{ rotation }}° 缩放({{ scaleX }}, {{ scaleY }})</p>
          </div>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
// 顶点着色器
attribute vec2 a_position;
uniform vec2 u_translation;
uniform vec2 u_scale;
uniform float u_rotation;

void main() {
  vec2 scaledPosition = a_position * u_scale;
  float s = sin(u_rotation);
  float c = cos(u_rotation);
  mat2 rotationMatrix = mat2(c, -s, s, c);
  vec2 rotatedPosition = rotationMatrix * scaledPosition;
  vec2 position = rotatedPosition + u_translation;
  gl_Position = vec4(position, 0, 1);
}

// 片元着色器
precision mediump float;
void main() {
  gl_FragColor = vec4(1, 0, 0.5, 1);
}

// JavaScript代码
const positions = new Float32Array([
  -0.8, 0.8,    -0.6, 0.8,    -0.8, -0.8,    // 字母F的主干
  -0.8, -0.8,   -0.6, 0.8,    -0.6, -0.8,
  -0.6, 0.8,    -0.2, 0.8,    -0.6, 0.6,     // 顶部横线
  -0.6, 0.6,    -0.2, 0.8,    -0.2, 0.6,
  -0.6, 0.0,    -0.2, 0.0,    -0.6, -0.2,    // 中部横线
  -0.6, -0.2,   -0.2, 0.0,    -0.2, -0.2
]
]);

// 设置变换矩阵
gl.uniform2f(translationLocation, tx, ty);
gl.uniform2f(scaleLocation, sx, sy);
gl.uniform1f(rotationLocation, rotation);
        </code></pre>
      </div>
    </el-card>

    <el-card class="info-section">
      <template #header>
        <div class="card-header">
          <h3>矩阵变换说明</h3>
        </div>
      </template>
      <el-descriptions :column="1" border>
        <el-descriptions-item label="平移变换">
          通过uniform变量u_translation实现图形的平移
        </el-descriptions-item>
        <el-descriptions-item label="旋转变换">
          使用旋转矩阵实现图形的旋转，旋转角度由uniform变量u_rotation控制
        </el-descriptions-item>
        <el-descriptions-item label="缩放变换">
          通过uniform变量u_scale实现图形的缩放
        </el-descriptions-item>
      </el-descriptions>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

const canvas = ref<HTMLCanvasElement | null>(null)
const translateX = ref(0)
const translateY = ref(0)
const rotation = ref(0)
const scaleX = ref(1)
const scaleY = ref(1)

const vertexShaderSource = `
  attribute vec2 a_position;
  uniform vec2 u_translation;
  uniform vec2 u_scale;
  uniform float u_rotation;

  void main() {
    vec2 scaledPosition = a_position * u_scale;
    float s = sin(u_rotation);
    float c = cos(u_rotation);
    mat2 rotationMatrix = mat2(c, -s, s, c);
    vec2 rotatedPosition = rotationMatrix * scaledPosition;
    vec2 position = rotatedPosition + u_translation;
    gl_Position = vec4(position, 0, 1);
  }
`

const fragmentShaderSource = `
  precision mediump float;
  void main() {
    gl_FragColor = vec4(1, 0, 0.5, 1);
  }
`

function createShader(gl: WebGLRenderingContext, type: number, source: string) {
  const shader = gl.createShader(type)
  if (!shader) return null
  gl.shaderSource(shader, source)
  gl.compileShader(shader)
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.error(gl.getShaderInfoLog(shader))
    gl.deleteShader(shader)
    return null
  }
  return shader
}

function createProgram(gl: WebGLRenderingContext, vertexShader: WebGLShader, fragmentShader: WebGLShader) {
  const program = gl.createProgram()
  if (!program) return null
  gl.attachShader(program, vertexShader)
  gl.attachShader(program, fragmentShader)
  gl.linkProgram(program)
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    console.error(gl.getProgramInfoLog(program))
    gl.deleteProgram(program)
    return null
  }
  return program
}

function draw() {
  if (!canvas.value) return
  const gl = canvas.value.getContext('webgl')
  if (!gl) return

  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource)
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource)
  if (!vertexShader || !fragmentShader) return

  const program = createProgram(gl, vertexShader, fragmentShader)
  if (!program) return

  gl.useProgram(program)

  const positions = new Float32Array([
    -0.8, 0.8,    -0.6, 0.8,    -0.8, -0.8,    // 字母F的主干
    -0.8, -0.8,   -0.6, 0.8,    -0.6, -0.8,
    -0.6, 0.8,    -0.2, 0.8,    -0.6, 0.6,     // 顶部横线
    -0.6, 0.6,    -0.2, 0.8,    -0.2, 0.6,
    -0.6, 0.0,    -0.3, 0.0,    -0.6, -0.2,    // 中部横线
    -0.6, -0.2,   -0.3, 0.0,    -0.3, -0.2
  ])

  const positionBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)

  const positionAttributeLocation = gl.getAttribLocation(program, 'a_position')
  gl.enableVertexAttribArray(positionAttributeLocation)
  gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0)

  const translationLocation = gl.getUniformLocation(program, 'u_translation')
  const scaleLocation = gl.getUniformLocation(program, 'u_scale')
  const rotationLocation = gl.getUniformLocation(program, 'u_rotation')

  gl.uniform2f(translationLocation, translateX.value, translateY.value)
  gl.uniform2f(scaleLocation, scaleX.value, scaleY.value)
  gl.uniform1f(rotationLocation, rotation.value * Math.PI / 180)

  gl.clearColor(0, 0, 0, 0)
  gl.clear(gl.COLOR_BUFFER_BIT)
  gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2)
}

onMounted(() => {
  draw()
})

function updateTransform() {
  draw()
}
</script>

<style lang="scss" scoped>
.matrix-2d {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;

  h1 {
    color: #303133;
    margin-bottom: 20px;
  }

  p {
    margin-bottom: 30px;
  }

  .demo-section,
  .code-section,
  .info-section {
    margin: 30px 0;

    .card-header {
      display: flex;
      align-items: center;

      h3 {
        margin: 0;
        color: #409EFF;
      }
    }
  }

  .demo-section {
    .demo-content {
      display: flex;
      gap: 20px;

      canvas {
        border: 1px solid #dcdfe6;
        background-color: #000;
      }

      .control-panel {
        flex: 1;
        padding: 20px;
        background-color: #f8f9fa;
        border-radius: 4px;

        .transform-info {
          margin-top: 20px;
          text-align: center;
          color: #606266;
          font-family: monospace;

          p {
            margin: 5px 0;
          }
        }
      }
    }
  }

  .code-section,
  .info-section {
    .code-content {
      background-color: #f8f9fa;
      padding: 15px;
      border-radius: 4px;

      code {
        font-family: monospace;
        color: #409EFF;
      }

      pre {
        margin: 0;
        white-space: pre-wrap;
      }
    }
  }
}
</style>
